import worker from '@ohos.worker';
import Queue from '@ohos.util.Queue';
import fs from '@ohos.file.fs';
import { Log } from '../utils/Log';
import media from '@ohos.multimedia.media';
import avcodec from 'libavcodec.so'
import {Message,ActionData} from './Message'
import image from '@ohos.multimedia.image';
import mediaLibrary from '@ohos.multimedia.mediaLibrary';

let venc;
let nv12data = new Uint8Array(1.5*640*480);
let TAG = "YW_work";
const workerPort = worker.workerPort;
let outputStream:fs.Stream;
let codecBufferQueue:Queue<ArrayBuffer>;
let system = true;
let encoderIsRunning = false;
let internalId;
let parent = worker.workerPort;


workerPort.onmessage = function (data) {
    Log.d(TAG,"data="+JSON.stringify(data))
    let actionData = data.data;
    if(actionData.type === Message.WRITE_BUFFER){
        writeBufferToFile(actionData.data)
    } else
    if(actionData.type === Message.INIT_ENCODER){
        initEncoder()
    } else
    if(actionData.type === Message.STOP_ENCODER){
        stopEncoder()
    }
    if(actionData.type === Message.SEND_IMAGE_CONFIG){
        initPreviewImageReceiver(actionData.width,actionData.height,actionData.format)
    }
    if(actionData.type === Message.READ_YUV_FILE){
        readYuvFile()
    }
}

function sendFrameToEncoder(buffer:Uint8Array){
    if(venc != undefined && encoderIsRunning) {
        let timestamp = (new Date()).getTime();
        venc.sendFrameData(buffer, buffer.byteLength, timestamp);
    }
}

function addBufferToQueue(buffer:ArrayBuffer){
    Log.d(TAG,"addBufferToQueue")
    if(codecBufferQueue == undefined){
        codecBufferQueue = new Queue<ArrayBuffer>();
    }
    codecBufferQueue.add(buffer);
}
function writeBufferToFile(buffer:Uint8Array){
    Log.d(TAG,"YW_ writeBufferToFile buffer = "+buffer)
    Log.d(TAG,"YW_ writeBufferToFile path = "+globalThis.appFilePath)
    if(buffer === undefined) return;
    if(outputStream == undefined){
        try{
            outputStream = fs.createStreamSync('/data/storage/el2/base/haps/entry/files/640_480_nv12.h264', "w+");
        }catch (error){
            Log.e(TAG,"YW_ create outputStream fail error:"+JSON.stringify(error))
            return
        }
        Log.d(TAG,"YW_ create outputStream success")
    }
    //let buffer = codecBufferQueue.pop();
    try {
        outputStream.writeSync(buffer.buffer)
    }catch (error){
        Log.e(TAG,"YW_ create outputStream.writeSync fail error:"+JSON.stringify(error))
    }
}


function I420ToNV12(data:Uint8Array,width:number,height:number):Uint8Array{

    let frameSize = width*height;
    let u_start_pos = frameSize;
    let v_start_pos = frameSize+frameSize/4;

    for(let i = 0;i< frameSize;i++){
        nv12data[i] = data[i];
    }
    for(let j = 0;j<frameSize/4;j++){
        nv12data[frameSize+j*2] = data[frameSize+j];
        nv12data[frameSize+j*2+1] = data[frameSize+j+frameSize/4];
    }
    return nv12data;
}

function initEncoder(){
    if(system){
        venc = media.createVideoEncoder();
    } else {
        if(avcodec != undefined){
            venc = avcodec.createVideoEncoder();
        } else {
            Log.e(TAG,"avcodec is undefined");
        }
    }
    if(venc != undefined) {
        let avformat:media.AvFormat = {
            mime: media.CodecMimeType.VIDEO_AVC,
            width: 640,
            height: 480,
            pixelFormat: 2,
            frameRate: 20,
            encodeBitRate: 640 * 480 * 20 * 0.2,
            iFrameIntervalMs: 200,
            codecQuality: 0,
            codecProfile: 0,
            videoEncodeBitRateMode:media.VideoEncodeBitrateMode.CBR
        }
        venc.configure(avformat);
        venc.prepare();
        venc.start();
        encoderIsRunning = true;
        venc.on("OnOutputBufferAvailable", (data, size, pts, flags) => {
            Log.d(TAG, "ets OnOutputBufferAvailable");
            //writeBufferToFile(data);
        });
        venc.on("onError", (error) => {
            Log.d(TAG, "ets onError");
        });
        venc.on("OnOutputFormatChanged", (format) => {
            Log.d(TAG, "ets OnOutputFormatChanged");
        })
//        venc.on("codecDataArrival", () => {
//            Log.d(TAG, "ets codecDataArrival");
//        });
    }
}

function stopEncoder(){

    Log.d(TAG,"stopEncoder ");
    let ret = -1;
    if(venc != undefined){
        ret = venc.flush();
        Log.d(TAG,"venc.flush ret = "+ret);
        ret = venc.stop();
        encoderIsRunning = false;
        clearInterval(internalId);
        Log.d(TAG,"venc.stop ret = "+ret);
        ret = venc.reset();
        Log.d(TAG,"venc.reset ret = "+ret);
        ret = venc.release();
        Log.d(TAG,"venc.release ret = "+ret);
        venc == null;
    }
}

async function initPreviewImageReceiver(width:number,height:number,format:number){
    Log.d(TAG,"initPreviewImageReceiver width = "+width+" height = "+height+" format"+format);
    let receiver = image.createImageReceiver(width, height,format,8);
    Log.i(TAG,'before ImageReceiver check');
    if (receiver !== undefined) {
        Log.i(TAG,'ImageReceiver is ok');
        let previewSurfaceId = await receiver.getReceivingSurfaceId();
        Log.i(TAG,'ImageReceived id: ' + globalThis.previewSurfaceId);
        parent.postMessage(Message.initSurfaceIdMsg(previewSurfaceId))
    } else {
        Log.i(TAG, 'ImageReceiver is not ok');
    }

    receiver.on('imageArrival',()=>{
        Log.i(TAG,'imageArrival')
        receiver.readNextImageDirectly(<image.GuideImageListener>{
            imageArrival:dataArrival
        })

    })
}

function dataArrival(imageFormat: number, imageWidth: number, imageHeight: number, imageData: Uint8Array, imageDataSize: number):void {
    Log.i(TAG,"[PerformanceTest] dataArrival imageFormat = "+imageFormat+" imageWidth ="+imageWidth+" imageHeight ="+imageHeight);
    sendFrameToEncoder(imageData)
}

async function readYuvFile(){
    let file = fs.openSync("/data/storage/el2/base/haps/entry/files/test.yuv", fs.OpenMode.READ_ONLY);
    Log.i(TAG,"file open = "+file)
    let buf = new ArrayBuffer(640*480*1.5);
    let file_offset = 0;
    let readLen = fs.readSync(file.fd, buf, { offset: file_offset });
    //fs.closeSync(file);
    let databuffer = new Uint8Array(buf)
    Log.i(TAG,"readYuvFile: setTimeout")
    internalId = setInterval(()=>{
            file_offset += readLen;
            readLen = fs.readSync(file.fd, buf, { offset: file_offset });
            if(readLen <= 0 ){
                file_offset = 0;
            }
            //let timestamp = (new Date()).getTime();
            dataArrival(28,640,480,databuffer,640*480*1.5);
        },17)
    //fs.closeSync(file);
    Log.i(TAG,"read yuv file  success");
}

function delay(milliseconds : number){
    return new Promise(resolve => setTimeout(resolve, milliseconds));
}

